#pragma once
#ifndef __HWINSTRING_H__
#define __HWINSTRING_H__

#include "hwindef.h"
#include "hwinexception.h"


namespace harlinn
{
    namespace windows
    {


        // ----------------------------------------------------------------------
        // String
        //
        // The binary representation if this class is a pointer to a zero
        // terminated string or nullptr.
        // 
        // No data is allocated for empty strings, causing c_str() to return 
        // nullptr
        // ----------------------------------------------------------------------
        class String
        {
        public:
            typedef unsigned long long size_type;
            typedef wchar_t value_type;
            static const size_type npos = MAXDWORD64;
            static const wchar_t defaultPadCharacter = L'\x20';

            friend HWIN_EXPORT std::wostream& operator << ( std::wostream& os, const String& s);
        private:
            

            struct Buffer
            {
                size_type referenceCount;
                size_type length;
                wchar_t data[128];
            };

            wchar_t* data;

            Buffer* toBuffer() const
            {
                if(data)
                {
                    return (Buffer*)(((char*)data) - offsetof(Buffer,data));
                }
                return nullptr;
            }

            static Buffer* toBuffer(const wchar_t* data) 
            {
                if(data)
                {
                    return (Buffer*)(((char*)data) - offsetof(Buffer,data));
                }
                return nullptr;
            }

            static const size_type nonTextBufferByteCount = offsetof(Buffer,data) + sizeof(wchar_t);
            static const size_type minBufferByteCount = 64;
            static const size_type minReservedCharacters = (minBufferByteCount - nonTextBufferByteCount)/sizeof(wchar_t);


            static size_type allocationByteCount( size_type length )
            {
                if(length)
                {
                    if(length < minReservedCharacters)
                    {
                        return minBufferByteCount;
                    }

                    size_type bytesRequired = nonTextBufferByteCount + length*sizeof(wchar_t);

                    size_type remainder = bytesRequired % minBufferByteCount;
                    if (remainder != 0)
                    {
                        bytesRequired += minBufferByteCount - remainder;
                    }
                    return bytesRequired;
                }
                return 0;
            }

            static Buffer* allocate(size_type length)
            {
                if(length)
                {
                    size_type byteCount = allocationByteCount(length);
                    Buffer* result = (Buffer*)new char[byteCount];
                    result->referenceCount = 1;
                    result->length = length;
                    result->data[length] = 0;
                    
                    return result;
                }
                return nullptr;
            }

            Buffer* EnsureUnique()
            {
                Buffer* buffer = toBuffer();
                if(buffer)
                {
                    if(buffer->referenceCount > 1)
                    {
                        auto byteCount = allocationByteCount(buffer->length);

                        Buffer* newBuffer = (Buffer*)new char[byteCount];
                        newBuffer->referenceCount = 1;
                        newBuffer->length = buffer->length;
                        memcpy(newBuffer->data,buffer->data,buffer->length*sizeof(wchar_t));
                        newBuffer->data[buffer->length] = 0;
                        Release();
                        data = newBuffer->data;
                        return newBuffer;
                    }
                }
                return buffer;
            }


            Buffer* ReallocateUnique(size_type length)
            {
                Buffer* buffer = toBuffer();
                if(buffer)
                {
                    auto currentByteCount = allocationByteCount(buffer->length);
                    auto newByteCount = allocationByteCount(length);
                    if(!newByteCount)
                    {
                        Release();
                        return nullptr;
                    }
                    else
                    {
                        if((buffer->referenceCount > 1)||(currentByteCount != newByteCount))
                        {
                            Buffer* newBuffer = (Buffer*)new char[newByteCount];
                            newBuffer->referenceCount = 1;
                            newBuffer->length = length;
                            size_type copyCount = min(buffer->length,length)+2;
                            memcpy(newBuffer->data,buffer->data,copyCount*sizeof(wchar_t));
                            newBuffer->data[length] = 0;
                            Release();
                            data = newBuffer->data;
                            return newBuffer;
                        }
                        buffer->length = length;
                        buffer->data[length] = 0;
                    }
                }
                else
                {
                    buffer = allocate(length);
                    data = buffer->data;
                }
                return buffer;
            }


            void Release()
            {
                Buffer* buffer = toBuffer();
                if(buffer)
                {
                    LONG64 res = InterlockedDecrement64((LONG64*)&(buffer->referenceCount));
                    if(res == 0)
                    {
                        char* bytes = (char*)buffer;
                        delete bytes;
                    }
                    data = nullptr;
                }
            }

            void AddRef(const String& other)
            {
                if(other.data != data)
                {
                    Release();
                    if(other.data)
                    {
                        data = other.data;
                        Buffer* buffer = toBuffer();
                        InterlockedIncrement64((LONG64*)&(buffer->referenceCount));
                    }
                }
            }

            
            static int Compare(Buffer* buffer1, Buffer* buffer2)
            {
                if(buffer1 == buffer2)
                {
                    return 0;
                }
                if(buffer1 && buffer2)
                {
                    size_type compareLength = min(buffer1->length,buffer2->length);
                    for(size_type i = 0; i < compareLength; i++)
                    {
                        int res = int(buffer1->data[i]) - int(buffer2->data[i]);
                        if(res != 0)
                        {
                            return res;
                        }
                    }
                    if(buffer1->length > buffer2->length)
                    {
                        return 1;
                    }
                    if(buffer1->length < buffer2->length)
                    {
                        return -1;
                    }
                    return 0;
                }
                else if(buffer1)
                {
                    return 1;
                }
                else if(buffer2)
                {
                    return -1;
                }
                return 0;
            }

            static int Compare(Buffer* buffer1, const wchar_t* str)
            {
                if(buffer1 && str)
                {
                    size_type length = wcslen(str);
                    size_type compareLength = min(buffer1->length,length);
                    for(size_type i = 0; i < compareLength; i++)
                    {
                        int res = int(buffer1->data[i]) - int(str[i]);
                        if(res != 0)
                        {
                            return res;
                        }
                    }
                    if(buffer1->length > length)
                    {
                        return 1;
                    }
                    if(buffer1->length < length)
                    {
                        return -1;
                    }
                    return 0;
                }
                else if(buffer1)
                {
                    return 1;
                }
                else if(str)
                {
                    return -1;
                }
                return 0;
            }

        public:
            String()
                : data(nullptr)
            {}

            String(const String& other)
                : data(nullptr)
            {
                if(other.data)
                {
                    data = other.data;
                    Buffer* buffer = toBuffer();
                    InterlockedIncrement64((LONG64*)&(buffer->referenceCount));
                }
            }


            String(size_type length, wchar_t c)
                : data(nullptr)
            {
                if(length)
                {
                    ReallocateUnique(length);
                    std::fill(data,data+length,c);
                }
            }

            String(const wchar_t* str,size_type length, wchar_t padCharacter = defaultPadCharacter )
                : data(nullptr)
            {
                if(length)
                {
                    ReallocateUnique(length);
                    if(str)
                    {
                        //std::copy_n(str,length,data);
                        wmemcpy(data,str,length);
                    }
                    else
                    {
                        //std::fill(data,data+length,padCharacter);
                        wmemset(data,padCharacter,length);
                    }
                }
            }

            String(const wchar_t* str1,size_type length1, const wchar_t* str2,size_type length2, wchar_t padCharacter = defaultPadCharacter)
                : data(nullptr)
            {
                size_type totalLength = length1 + length2;
                if(totalLength)
                {
                    ReallocateUnique(totalLength);
                    if(str1)
                    {
                        //std::copy_n(str1,length1,data);
                        wmemcpy(data,str1,length1);
                    }
                    else
                    {
                        //std::fill(data,data+length1,padCharacter);
                        wmemset(data,padCharacter,length1);
                    }

                    if(str2)
                    {
                        //std::copy_n(str2,length2,&data[length1]);
                        wmemcpy(&data[length1],str2,length2);
                    }
                    else
                    {
                        //std::fill(data+length1,data+totalLength,padCharacter);
                        wmemset(data+length1,padCharacter,length2);
                    }
                }
            }


            String(const wchar_t* str1,size_type length1, const wchar_t* str2,size_type length2, const wchar_t* str3,size_type length3, wchar_t padCharacter = defaultPadCharacter)
                : data(nullptr)
            {
                size_type totalLength = length1 + length2 + length3;
                if(totalLength)
                {
                    ReallocateUnique(totalLength);
                    if(str1)
                    {
                        std::copy_n(str1,length1,data);
                    }
                    else
                    {
                        std::fill_n(data,length1,padCharacter);
                    }

                    if(str2)
                    {
                        std::copy_n(str2,length2,&data[length1]);
                    }
                    else
                    {
                        std::fill_n(data+length1,length2,padCharacter);
                    }
                    if(str3)
                    {
                        std::copy_n(str3,length3,&data[length1+length2]);
                    }
                    else
                    {
                        std::fill_n(data+length1+length2,length3,padCharacter);
                    }


                }
            }



            String(const wchar_t* str)
                : data(nullptr)
            {
                if(str)
                {
                    size_type length = wcslen(str);
                    if(length)
                    {
                        ReallocateUnique(length);
                        std::copy_n(str,length,data);
                    }
                }
            }


            String(String&& other)
                : data(nullptr)
            {
                data = other.data;
                other.data = nullptr;
            }

            ~String()
            {
                if(data)
                {
                    Release();
                }
            }

            String& operator = (const String& other)
            {
                AddRef(other);
                return *this;
            }


            String& operator = (const wchar_t* str)
            {
                if(!str)
                {
                    Release();
                }
                else
                {
                    if(str != data)
                    {
                        Buffer* thisBuffer = toBuffer();
                        if(thisBuffer && str >= data && str < data + thisBuffer->length)
                        {
                            size_type offset = str - data;
                            size_type length = thisBuffer->length - offset;
                            
                            EnsureUnique();
                            str = data + offset;
                            memmove(data,str,length*sizeof(wchar_t));
                            ReallocateUnique(length);
                        }
                        else
                        {
                            size_type length = wcslen(str);
                            thisBuffer = ReallocateUnique(length);
                            wmemcpy(data,str,length);
                            //std::copy_n(str,length,data);
                        }
                    }
                }
                return *this;
            }

            String& operator = (String&& other)
            {
                if(this != &other)
                {
                    data = other.data;
                    other.data = nullptr;
                }
                return *this;
            }

            int CompareTo(const String& other) const
            {
                return Compare(toBuffer(),other.toBuffer());
            }

            int CompareTo(const wchar_t* str) const
            {
                return Compare(toBuffer(),str);
            }


            bool operator == (const String& other) const { return CompareTo(other) == 0; }
            bool operator != (const String& other) const { return CompareTo(other) != 0; }
            bool operator <= (const String& other) const { return CompareTo(other) <= 0; }
            bool operator <  (const String& other) const { return CompareTo(other) <  0; }
            bool operator >= (const String& other) const { return CompareTo(other) >= 0; }
            bool operator >  (const String& other) const { return CompareTo(other) >  0; }

            bool operator == (const wchar_t* str) const { return CompareTo(str) == 0; }
            bool operator != (const wchar_t* str) const { return CompareTo(str) != 0; }
            bool operator <= (const wchar_t* str) const { return CompareTo(str) <= 0; }
            bool operator <  (const wchar_t* str) const { return CompareTo(str) <  0; }
            bool operator >= (const wchar_t* str) const { return CompareTo(str) >= 0; }
            bool operator >  (const wchar_t* str) const { return CompareTo(str) >  0; }


            String& Append(const String& other)
            {
                Buffer* otherBuffer = other.toBuffer();
                if(otherBuffer && otherBuffer->length)
                {
                    Buffer* thisBuffer = toBuffer();
                    if(!thisBuffer)
                    {
                        data = otherBuffer->data;
                        InterlockedIncrement64((LONG64*)&(otherBuffer->referenceCount));
                    }
                    else
                    {
                        size_type oldLength = thisBuffer->length;
                        if(thisBuffer == otherBuffer)
                        {
                            thisBuffer = ReallocateUnique(thisBuffer->length + thisBuffer->length);
                            //std::copy_n(thisBuffer->data,oldLength,&thisBuffer->data[oldLength]);
                            wmemcpy(&thisBuffer->data[oldLength],thisBuffer->data,oldLength);
                        }
                        else
                        {
                            thisBuffer = ReallocateUnique(thisBuffer->length + otherBuffer->length);
                            //std::copy_n(otherBuffer->data,otherBuffer->length,&thisBuffer->data[oldLength]);
                            wmemcpy(&thisBuffer->data[oldLength],otherBuffer->data,otherBuffer->length);
                        }
                    }
                }
                return *this;
            }


            String& Append(const wchar_t* str, size_type length)
            {
                if(str)
                {
                    Buffer* thisBuffer = toBuffer();
                    if(!thisBuffer)
                    {
                        if(length)
                        {
                            ReallocateUnique(length);
                            std::copy_n(str,length,data);
                        }
                    }
                    else if((str == data)&&(length >= thisBuffer->length))
                    {
                        size_type oldLength = thisBuffer->length;
                        
                        thisBuffer = ReallocateUnique(thisBuffer->length + thisBuffer->length);
                        std::copy_n(thisBuffer->data,oldLength,&thisBuffer->data[oldLength]);

                    }
                    else if(str > data && str < (data + thisBuffer->length))
                    {
                        size_type offset = str - data;
                        if(length > (thisBuffer->length - offset))
                        {
                            length = thisBuffer->length - offset;
                        }
                        
                        size_type oldLength = thisBuffer->length;
                        
                        thisBuffer = ReallocateUnique(oldLength + length);
                        str = data + offset;
                        std::copy_n(str,length,&thisBuffer->data[oldLength]);
                    }
                    else
                    {
                        
                        Buffer* thisBuffer = toBuffer();
                        size_type oldLength = thisBuffer->length;
                    
                        thisBuffer = ReallocateUnique(oldLength + length);
                        std::copy_n(str,length,&thisBuffer->data[oldLength]);
                    }
                }
                return *this;
            }


            String& Append(const wchar_t* str)
            {
                if(str && str[0])
                {
                    Buffer* thisBuffer = toBuffer();
                    if(!thisBuffer)
                    {
                        size_type length = wcslen(str);
                        if(length)
                        {
                            ReallocateUnique(length);
                            std::copy_n(str,length,data);
                        }
                    }
                    else if(str == data)
                    {
                        size_type oldLength = thisBuffer->length;
                        
                        thisBuffer = ReallocateUnique(thisBuffer->length + thisBuffer->length);
                        std::copy_n(thisBuffer->data,oldLength,&thisBuffer->data[oldLength]);

                    }
                    else if(str > data && str < (data + thisBuffer->length))
                    {
                        size_type offset = str - data;
                        size_type length = thisBuffer->length - offset;
                        
                        size_type oldLength = thisBuffer->length;
                        
                        
                        thisBuffer = ReallocateUnique(thisBuffer->length + length);
                        str = data + offset;
                        std::copy_n(str,length,&thisBuffer->data[oldLength]);
                    }
                    else
                    {
                        size_type length = wcslen(str);
                        Buffer* thisBuffer = toBuffer();
                        size_type oldLength = thisBuffer->length;
                    
                        thisBuffer = ReallocateUnique(thisBuffer->length + length);
                        std::copy_n(str,length,&thisBuffer->data[oldLength]);
                    }
                }
                return *this;
            }

            String& Append(wchar_t c, size_type numberOfTimes)
            {
                Buffer* thisBuffer = toBuffer();
                if(!thisBuffer)
                {
                    thisBuffer = ReallocateUnique(numberOfTimes);
                    //std::fill_n(data,numberOfTimes,c);
                    wmemset(thisBuffer->data,c,numberOfTimes);
                }
                else
                {
                    size_type oldLength = thisBuffer->length;
                    thisBuffer = ReallocateUnique(oldLength+numberOfTimes);
                    wmemset(&thisBuffer->data[oldLength],c,numberOfTimes);
                    //std::fill_n(&thisBuffer->data[oldLength],numberOfTimes,c);
                }
                return *this;
            }


            String& Append(wchar_t c)
            {
                Buffer* thisBuffer = toBuffer();
                if(!thisBuffer)
                {
                    thisBuffer = ReallocateUnique(1);
                    data[0] = c;
                }
                else
                {
                    size_type oldLength = thisBuffer->length;
                    thisBuffer = ReallocateUnique(oldLength+1);
                    data[oldLength] = c;
                }
                return *this;
            }


            String Appended(const String& other) const
            {
                Buffer* thisBuffer = toBuffer();
                Buffer* otherBuffer = other.toBuffer();
                if(thisBuffer && otherBuffer)
                {
                    String result(thisBuffer->data,thisBuffer->length,otherBuffer->data,otherBuffer->length);
                    return result;
                }
                else if(thisBuffer)
                {
                    String result(*this);
                    return result;
                }
                return other;
            }

            String Appended(const wchar_t* str) const
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer && str && str[0])
                {
                    size_type length = wcslen(str);
                    String result(thisBuffer->data,thisBuffer->length,str,length);
                    return result;
                }
                else if(thisBuffer)
                {
                    String result(*this);
                    return result;
                }
                else if(str && str[0])
                {
                    return String(str);
                }
                return String();
            }

            String Appended(wchar_t c, size_type numberOfTimes) const
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    String result(thisBuffer->data,thisBuffer->length,nullptr,numberOfTimes,c);
                    return result;
                }
                else
                {
                    String result(nullptr,numberOfTimes,c);
                    return result;
                }
            }


            String& operator += (const String& other)
            {
                return Append(other);
            }

            String& operator += (const wchar_t* str)
            {
                return Append(str);
            }

            String& operator += (const wchar_t c)
            {
                return Append(c);
            }


            friend String operator + (const String& str1,const String& str2)
            {
                return str1.Appended(str2);
            }

            friend String operator + (const String& str1,const wchar_t* str2)
            {
                return str1.Appended(str2);
            }

            friend String operator + (const String& str,const wchar_t c)
            {
                return str.Appended(c,1);
            }

            operator bool() const
            {
                return data != nullptr;
            }

            wchar_t& operator[](size_type index)
            {
#ifdef _DEBUG
                Buffer* buffer = EnsureUnique();
                if(!buffer || index >= buffer->length)
                {
                    throw ArgumentOutOfRangeException("index");
                }
                return data[index];
#else
                return data[index];
#endif
            }


            wchar_t operator[](size_type index) const
            {
#ifdef _DEBUG
                Buffer* buffer = toBuffer();
                if(!buffer || index >= buffer->length)
                {
                    throw ArgumentOutOfRangeException("index");
                }
                return data[index];
#else
                return data[index];
#endif
            }


            String& SetLength(size_type newLength)
            {
                Buffer* buffer = toBuffer();
                if(buffer && buffer->length != newLength)
                {
                    ReallocateUnique(newLength);
                }
                else if(newLength)
                {
                    ReallocateUnique(newLength);
                }
                return *this;
            }

            String& SetString(const wchar_t* text, size_type textLength)
            {
                Buffer* buffer = ReallocateUnique(textLength);
                if(buffer)
                {
                    wmemcpy(buffer->data,text,textLength);
                }
                return *this;
            }


            size_type length() const
            {
                Buffer* buffer = toBuffer();
                if(buffer)
                {
                    return buffer->length;
                }
                return 0;
            }

            size_type Length() const
            {
                Buffer* buffer = toBuffer();
                if(buffer)
                {
                    return buffer->length;
                }
                return 0;
            }


            const wchar_t* c_str() const
            {
                return data;
            }

            wchar_t* c_str()
            {
                EnsureUnique();
                return data;
            }

            const wchar_t* begin() const { return data; }
            const wchar_t* end() const 
            { 
                if(data)
                {
                    Buffer* buffer = toBuffer();
                    return data + buffer->length;
                }
                else 
                {
                    return data;
                }
            }

            const wchar_t* cbegin() const { return data; }
            const wchar_t* cend() const 
            { 
                if(data)
                {
                    Buffer* buffer = toBuffer();
                    return data + buffer->length;
                }
                else 
                {
                    return data;
                }
            }

            wchar_t* begin() { EnsureUnique(); return data; }
            wchar_t* end()
            { 
                if(data)
                {
                    Buffer* buffer = EnsureUnique();
                    return data + buffer->length;
                }
                else 
                {
                    return data;
                }
            }


            size_type IndexOfAnyOf ( const wchar_t *searchChars, size_type numberOfSearchChars, size_type start) const
            {
                Buffer* thisBuffer = toBuffer();
                
                if ( thisBuffer && searchChars && numberOfSearchChars )
                {
                    while(start < thisBuffer->length)
                    {
                        auto *p = wmemchr( searchChars , thisBuffer->data[start], numberOfSearchChars );
                        if(p)
                        {
                            return start;
                        }
                        start++;
                    }
                }
                return npos;
            }

            size_type IndexOfAnyOf ( const String& searchChars, size_type start = 0) const
            {
                Buffer* searchCharBuffer = searchChars.toBuffer();
                if(searchCharBuffer)
                {
                    return IndexOfAnyOf(searchCharBuffer->data,searchCharBuffer->length, start);
                }
                return npos;
            }

            size_type IndexOfAnyOf( const wchar_t* searchChars, size_type start = 0) const
            {
                if(searchChars && searchChars[0])
                {
                    size_type length = wcslen(searchChars);
                    return IndexOfAnyOf(searchChars ,length, start);
                }
                return npos;
            }


            size_type IndexOfAnyBut ( const wchar_t *searchChars, size_type numberOfSearchChars, size_type start) const
            {
                Buffer* thisBuffer = toBuffer();
                
                if ( thisBuffer && searchChars && numberOfSearchChars )
                {
                    while(start < thisBuffer->length)
                    {
                        auto *p = wmemchr( searchChars , thisBuffer->data[start], numberOfSearchChars );
                        if(!p)
                        {
                            return start;
                        }
                        start++;
                    }
                }
                return npos;
            }


            size_type IndexOfAnyBut ( const String& searchChars, size_type start = 0) const
            {
                Buffer* searchCharBuffer = searchChars.toBuffer();
                if(searchCharBuffer)
                {
                    return IndexOfAnyBut(searchCharBuffer->data,searchCharBuffer->length, start);
                }
                return npos;
            }

            size_type IndexOfAnyBut( const wchar_t* searchChars, size_type start = 0) const
            {
                if(searchChars && searchChars[0])
                {
                    size_type length = wcslen(searchChars);
                    return IndexOfAnyBut(searchChars ,length, start);
                }
                return npos;
            }


            size_type LastIndexOfAnyOf ( const wchar_t *searchChars, size_type numberOfSearchChars, size_type start) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer && numberOfSearchChars)
                {
                    if(start >= thisBuffer->length)
                    {
                        start = thisBuffer->length - 1;
                    }
                    do 
                    {
                        auto *p = wmemchr( searchChars, thisBuffer->data[start], numberOfSearchChars );
                        if(p)
                        {
                            return start;
                        }
                    } while(start--);
                }
                return npos;
            }

            size_type LastIndexOfAnyOf( const String& searchChars, size_type start = npos) const
            {
                Buffer* searchCharBuffer = searchChars.toBuffer();
                if(searchCharBuffer)
                {
                    return LastIndexOfAnyOf(searchCharBuffer->data,searchCharBuffer->length, start);
                }
                return npos;
            }

            size_type LastIndexOfAnyOf( const wchar_t* searchChars, size_type start = npos) const
            {
                if(searchChars && searchChars[0])
                {
                    size_type length = wcslen(searchChars);
                    return LastIndexOfAnyOf(searchChars ,length, start);
                }
                return npos;
            }


            size_type LastIndexOfAnyBut ( const wchar_t *searchChars, size_type numberOfSearchChars, size_type start ) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer )
                {
                    if(start >= thisBuffer->length)
                    {
                        start = thisBuffer->length - 1;
                    }
                    do 
                    {
                        auto *p = wmemchr( searchChars, thisBuffer->data[start], numberOfSearchChars );
                        if(!p)
                        {
                            return start;
                        }
                    } while(start--);
                }
                return npos;
            }


            size_type LastIndexOfAnyBut( const String& searchChars, size_type start = npos) const
            {
                Buffer* searchCharBuffer = searchChars.toBuffer();
                if(searchCharBuffer)
                {
                    return LastIndexOfAnyBut(searchCharBuffer->data,searchCharBuffer->length, start);
                }
                return npos;
            }

            size_type LastIndexOfAnyBut( const wchar_t* searchChars, size_type start = npos) const
            {
                if(searchChars && searchChars[0])
                {
                    size_type length = wcslen(searchChars);
                    return LastIndexOfAnyBut(searchChars ,length, start);
                }
                return npos;
            }


            size_type IndexOf( const wchar_t *searchString, size_type searchStringLength, size_type start) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer && ( start < thisBuffer->length ) )
                {
                    if ( searchStringLength == 1 )
                    {
                        auto *p = wmemchr( &thisBuffer->data[start], *searchString, thisBuffer->length - start );
                        if(p)
                        {
                            return p - thisBuffer->data;
                        }
                    }
                    else if ( searchStringLength )
                    {
                        while ( (start + searchStringLength) <= thisBuffer->length )
                        {
                            if ( wmemcmp( &thisBuffer->data[start], searchString, searchStringLength ) == 0 )
                            {
                                return start;
                            }
                            start++;
                        }
                    }
                }
                return npos;
            }

            size_type IndexOf( const String& searchString, size_type start = 0) const
            {
                Buffer* searchStringBuffer = searchString.toBuffer();
                if(searchStringBuffer)
                {
                    return IndexOf(searchStringBuffer->data,searchStringBuffer->length, start);
                }
                return npos;
            }

            size_type IndexOf( const wchar_t* searchString, size_type start = 0) const
            {
                Buffer* thisBuffer = toBuffer();
                if(searchString && searchString[0] && thisBuffer && start < thisBuffer->length)
                {
                    const wchar_t* pStart = &thisBuffer->data[start];
                    const wchar_t* pEnd = &thisBuffer->data[thisBuffer->length];
                    while(pStart < pEnd)
                    {
                        auto p = wmemchr( pStart, *searchString, pEnd - pStart );
                        //auto *p = wcschr( pStart, *searchString );
                        if(p)
                        {
                            const wchar_t* pSearchChar = searchString+1;
                            const wchar_t* pContent = p + 1;
                            while(*pSearchChar)
                            {
                                if(*pContent != *pSearchChar)
                                {
                                    break;
                                }
                                pContent++;
                                pSearchChar++;
                            }
                            if(!(*pSearchChar))
                            {
                                return p - thisBuffer->data;
                            }
                            pStart = p+1;
                        }
                        else
                        {
                            return npos;
                        }
                    }
                }
                return npos;
            }

            size_type IndexOf( const wchar_t c, size_type start = 0) const
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer && start < thisBuffer->length)
                {                                                                        
                    auto p = wmemchr( &data[start], c, thisBuffer->length - start);
                    if(p)
                    {
                        return p - data;
                    }
                }
                return npos;
            }


            size_type IndexOf( bool ( *test )(wchar_t ) , size_type start = 0) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer )
                {
                    while( start < thisBuffer->length )
                    {
                        wchar_t c = thisBuffer->data[start];
                        if(test(c))
                        {
                            return start;
                        }
                        start++;
                    }
                }
                return npos;
            }

            size_type IndexOf( bool ( *test )(const wchar_t*, size_type length ) , size_type start = 0) const
            {
                Buffer* thisBuffer = toBuffer();
                if (( thisBuffer ) && ( start < thisBuffer->length ))
                {
                    wchar_t* pStart = &thisBuffer->data[start];
                    size_type remainingLength = thisBuffer->length - start;
                    while( remainingLength )
                    {
                        if(test(pStart,remainingLength))
                        {
                            return start;
                        }
                        remainingLength--;
                        pStart++;
                    }
                }
                return npos;
            }

            size_type IndexOf( bool ( *test )(const wchar_t*, const wchar_t* ) , size_type start = 0) const
            {
                Buffer* thisBuffer = toBuffer();
                if (( thisBuffer ) && ( start < thisBuffer->length ))
                {
                    const wchar_t* pStart = &thisBuffer->data[start];
                    const wchar_t* pEnd = end();
                    while( pStart < pEnd )
                    {
                        if(test(pStart,pEnd))
                        {
                            return start;
                        }
                        pStart++;
                    }
                }
                return npos;
            }




            size_type LastIndexOf( const wchar_t *searchString, size_type searchStringLength, size_type start ) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer && (searchStringLength <= thisBuffer->length))
                {
                    if(start > (thisBuffer->length - searchStringLength))
                    {
                        start = thisBuffer->length - searchStringLength;
                    }

                    if ( searchStringLength == 1 )
                    {
                        do
                        {
                            if(thisBuffer->data[start] == *searchString)
                            {
                                return start;
                            }
                        }while(start--);
                    }
                    else if ( searchStringLength )
                    {
                        do
                        {
                            if ( wmemcmp( &thisBuffer->data[start], searchString, searchStringLength ) == 0 )
                            {
                                return start;
                            }
                        } while(start--);
                    }
                }
                return npos;
            }

            size_type LastIndexOf( const String& searchString, size_type start = npos) const
            {
                Buffer* searchStringBuffer = searchString.toBuffer();
                if(searchStringBuffer)
                {
                    return LastIndexOf(searchStringBuffer->data,searchStringBuffer->length, start);
                }
                return npos;
            }

            size_type LastIndexOf( const wchar_t* searchString, size_type start = npos) const
            {
                if(searchString && searchString[0])
                {
                    size_type length = wcslen(searchString);
                    return LastIndexOf(searchString ,length, start);
                }
                return npos;
            }

            size_type LastIndexOf( wchar_t c, size_type start = npos ) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer)
                {
                    if(start >= thisBuffer->length)
                    {
                        start = thisBuffer->length - 1;
                    }

                    do
                    {
                        if(thisBuffer->data[start] == c)
                        {
                            return start;
                        }
                    }while(start--);
                }
                return npos;
            }


            size_type LastIndexOf( bool ( *test )(wchar_t ) , size_type start = npos) const
            {
                Buffer* thisBuffer = toBuffer();
                if ( thisBuffer )
                {
                    if(start >= (thisBuffer->length))
                    {
                        start = thisBuffer->length - 1;
                    }

                    do
                    {
                        wchar_t c = thisBuffer->data[start];
                        if(test(c))
                        {
                            return start;
                        }
                    }while(start--);

                }
                return npos;
            }


            size_type LastIndexOf( bool ( *test )(const wchar_t*, size_type length ) , size_type start = npos) const
            {
                Buffer* thisBuffer = toBuffer();
                if (( thisBuffer ) && ( start < thisBuffer->length ))
                {
                    if(start >= (thisBuffer->length))
                    {
                        start = thisBuffer->length - 1;
                    }

                    const wchar_t* pStart = &thisBuffer->data[start];
                    size_type remainingLength = thisBuffer->length - start;

                    do
                    {
                        if(test(pStart,remainingLength))
                        {
                            return start;
                        }
                        remainingLength++;
                    }while(data != pStart--);
                }
                return npos;
            }


            size_type LastIndexOf( bool ( *test )(const wchar_t*, const wchar_t*) , size_type start = npos) const
            {
                Buffer* thisBuffer = toBuffer();
                if (( thisBuffer ) && ( start < thisBuffer->length ))
                {
                    if(start >= (thisBuffer->length))
                    {
                        start = thisBuffer->length - 1;
                    }

                    const wchar_t* pStart = &thisBuffer->data[start];
                    const wchar_t* pEnd = end();
                    do
                    {
                        if(test(pStart,pEnd))
                        {
                            return start;
                        }
                    }while(data != pStart--);
                }
                return npos;
            }

            bool IsControl(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswcntrl(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsDigit(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswdigit(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsLetter(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswalpha(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsLetterOrDigit(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswalnum(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsLower(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswlower(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsUpper(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswupper(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsPunctuation(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswpunct(thisBuffer->data[position]) != 0;
                }
                return false;
            }

            bool IsWhiteSpace(size_type position) 
            {
                Buffer* thisBuffer = toBuffer();
                if((thisBuffer)&&(position < thisBuffer->length))
                {
                    return iswspace(thisBuffer->data[position]) != 0;
                }
                return false;
            }


            bool StartsWith(const wchar_t* str) const
            {
                if(data && str)
                {
                    const wchar_t* p = data;
                    while(*p && *str)
                    {
                        if(*p != *str)
                        {
                            break;
                        }
                        p++;
                        str++;
                    }
                    return *str == L'\x00';
                }
                return false;
            }

            bool StartsWith(const String& str) const
            {
                return StartsWith(str.data);
            }


            HWIN_EXPORT const String& CopyTo( wchar_t* buffer, size_type bufferSize, size_type start = 0, wchar_t padCharacter = defaultPadCharacter ) const;

            String SubString ( size_type start, size_type length = npos) const
            {
                Buffer* thisBuffer = toBuffer();
                if( thisBuffer && (start < thisBuffer->length))
                {
                    if(length > (thisBuffer->length - start))
                    {
                        length = thisBuffer->length - start;
                    }
                    if((start == 0)&&(length == thisBuffer->length))
                    {
                        return String(*this);
                    }
                    else
                    {
                        return String(thisBuffer->data+start,length);
                    }    
                }
                else
                {
                    return String();
                }
            }

            String& UpperCase()
            {
                Buffer* thisBuffer = EnsureUnique();
                if(thisBuffer)
                {
                    CharUpperBuffW(thisBuffer->data,DWORD(thisBuffer->length));
                }
                return *this;
            }

            String& LowerCase()
            {
                Buffer* thisBuffer = EnsureUnique();
                if(thisBuffer)
                {
                    CharLowerBuffW(thisBuffer->data,DWORD(thisBuffer->length));
                }
                return *this;
            }

            String& Remove(size_type start, size_type length = npos)
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    if((start == 0) && (length >= thisBuffer->length))
                    {
                        Release();
                    }
                    else if( start < thisBuffer->length )
                    {
                        if( length >= thisBuffer->length - start)
                        {
                            ReallocateUnique(start);
                        }
                        else
                        {
                        
                            thisBuffer = EnsureUnique();

                            memmove(&thisBuffer->data[start],&thisBuffer->data[start+length], (thisBuffer->length - (start+length))*sizeof(wchar_t));

                            ReallocateUnique(thisBuffer->length-length);
                        }

                    }
                }
                return *this;
            }

            String& RemoveRange(size_type start, size_type end)
            {
                if(start < end)
                {
                    Remove(start, end - start);
                }
                return *this;
            }


            String& Keep(size_type start, size_type length = npos)
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    if((start == 0)&&(length >= thisBuffer->length))
                    {
                        return *this;
                    }
                    if(start >= thisBuffer->length)
                    {
                        Release();
                        return *this;
                    }
                    if(length > (thisBuffer->length - start))
                    {
                        length = thisBuffer->length - start;
                    }

                    thisBuffer = EnsureUnique();

                    memmove(thisBuffer->data,&thisBuffer->data[start], (length*sizeof(wchar_t)));

                    ReallocateUnique(length);

                }
                return *this;
            }

            String& KeepRange(size_type start, size_type end)
            {
                if(start < end)
                {
                    Keep(start, end - start);
                }
                else
                {
                    Release();
                }
                return *this;
            }

            String& Insert( const wchar_t* text, size_type textLength, size_type position )
            {
                if(text && textLength)
                {
                    Buffer* thisBuffer = toBuffer();
                    if(thisBuffer)
                    {
                        if(position >= thisBuffer->length )
                        {
                            Append(text, textLength);
                        }
                        else
                        {
                            if((text >= data) && (text < (data + thisBuffer->length)))
                            {
                                size_type offset = text - data;
                                if(textLength > (thisBuffer->length - offset))
                                {
                                    textLength = thisBuffer->length - offset;
                                }
                                String s(text,textLength);
                                size_type oldLength = thisBuffer->length;
                                thisBuffer = ReallocateUnique(oldLength+textLength);
                                memmove(&thisBuffer->data[position+textLength],&thisBuffer->data[position],(oldLength - position)* sizeof(wchar_t));
                                memmove(&thisBuffer->data[position], s.c_str() ,textLength* sizeof(wchar_t));
                            }
                            else
                            {
                                size_type oldLength = thisBuffer->length;
                                thisBuffer = ReallocateUnique(oldLength+textLength);
                                memmove(&thisBuffer->data[position+textLength],&thisBuffer->data[position],(oldLength - position)* sizeof(wchar_t));
                                memcpy(&thisBuffer->data[position], text ,textLength* sizeof(wchar_t));
                            }
                        }
                    }
                    else
                    {
                        thisBuffer = allocate(textLength);
                        memmove(thisBuffer->data,text, textLength * sizeof(wchar_t));
                    }
                }
                return *this;
            }


            String& Insert( const String& text, size_type position = 0)
            {
                Buffer* textBuffer = text.toBuffer();
                if(textBuffer)
                {
                    Insert(textBuffer->data,textBuffer->length,position);
                }
                return *this;
            }

            String& Insert( const wchar_t* text, size_type position = 0)
            {
                
                if(text && text[0])
                {
                    size_type length = wcslen(text);
                    Insert(text,length,position);
                }
                return *this;
            }



            String& TrimRight(const wchar_t* charactersToRemove, size_type numberOfCharactersToRemove)
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer && charactersToRemove && numberOfCharactersToRemove)
                {
                    auto last = thisBuffer->length - 1;
                    do
                    {
                        auto *p = wmemchr( charactersToRemove, thisBuffer->data[last], numberOfCharactersToRemove );
                        if(!p)
                        {
                            last++;
                            if(last == thisBuffer->length)
                            {
                                break;
                            }
                            thisBuffer = ReallocateUnique(last);
                            break;
                        }
                    }while(last--);
                    if(last == size_type(-1))
                    {
                        Release();
                    }
                }
                return *this;
            }

            String& TrimRight(const String& charactersToRemove)
            {
                return TrimRight(charactersToRemove.c_str(),charactersToRemove.length());
            }


            
            String& TrimRight()
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    auto last = thisBuffer->length - 1;
                    do
                    {
                        auto res = iswspace(thisBuffer->data[last]);
                        if(!res)
                        {
                            last++;
                            if(last == thisBuffer->length)
                            {
                                break;
                            }
                            thisBuffer = ReallocateUnique(last);
                            break;
                        }
                    }while(last--);
                    if(last == size_type(-1))
                    {
                        Release();
                    }
                }
                return *this;
            }

            String& TrimLeft()
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    for (size_type i = 0; i < thisBuffer->length; i++ )
                    {
                        auto res = iswspace(thisBuffer->data[i]);
                        if(!res)
                        {
                            Keep(i);
                            return *this;
                        }
                    }
                    Release();
                }
                return *this;
            }

            String& Trim()
            {
                Buffer* thisBuffer = toBuffer();
                if(thisBuffer)
                {
                    for (size_type i = 0; i < thisBuffer->length; i++ )
                    {
                        auto res = iswspace(thisBuffer->data[i]);
                        if(!res)
                        {
                            auto last = thisBuffer->length - 1;
                            do
                            {
                                auto res = iswspace(thisBuffer->data[last]);
                                if(!res)
                                {
                                    last++;
                                    KeepRange(i,last);
                                    break;
                                }
                            }while(last--);
                            
                            return *this;
                        }
                    }
                    Release();
                }
                return *this;
            }

            void swap(String& s)
            {
                auto tmp = data;
                data = s.data;
                s.data = tmp;
            }

            static void swap(String& a,String& b)
            {
                a.swap(b);
            }

			static String Format(const wchar_t* fmt, ...)
			{
				va_list args;
				va_start(args,fmt);
				int length = _vscwprintf( fmt, args );

				if (length < 0)
				{
					throw ArgumentException("fmt");
				}

				String result( String::size_type(length) ,L'\x0');
				vswprintf_s( result.c_str(), length+1, fmt, args );
				va_end(args);
				return result;
			}

        };

		




        HWIN_EXPORT String to_string(const char* str);
        HWIN_EXPORT String to_string(const wchar_t* str);


    };
};




#endif // __HWINSTRING_H__
